using System.Collections.Generic;
using System.Collections;
using System;
using System.Reflection;

namespace ObjectTransposeApi.Handler.Expand
{
	/// <summary/>
	internal class DictionaryExpandHandler : AbstractExpandHandler
	{
        /// <summary/>
        internal override IEnumerable<object> Process(object value)
        {
            Type type = value.GetType();

            // if the value a dictionary then handle it ... (remember the type has the enumerable interface)
            if (!DictionaryExpandHandler.IsDictionary(type))
            {
                return base._successor.Process(value);
            }

            // the error handling has been left out in the following relfection calls .. the type has 
            // already been checked so unless the type changes, the code will work 

            // return the public instance setter properties
            PropertyInfo valuesPropertyInfo = value.GetType().GetProperty(
                Constant.PROPERTY_VALUES,
                BindingFlags.Public | BindingFlags.Instance | BindingFlags.GetProperty);

            // invoke the values get property
            object dictionaryValueCollection = valuesPropertyInfo.GetValue(value, null);

            // fetch the get enumerator method
            MethodInfo getEnumeratorMethodInfo = dictionaryValueCollection.GetType().GetMethod(
                Constant.METHOD_GET_ENUMERATOR, 
                new Type[] { });

            // invoke the get enumerator method
            object dictionaryEnumerator = getEnumeratorMethodInfo.Invoke(
                dictionaryValueCollection,
                new object[] { });

            // fetch the current property
            PropertyInfo currentPropertyInfo = dictionaryEnumerator.GetType().GetProperty(
                Constant.PROPERTY_CURRENT,
                BindingFlags.Public | BindingFlags.Instance | BindingFlags.GetProperty);

            // fetch the move next property
            MethodInfo moveNextMethodInfo = dictionaryEnumerator.GetType().GetMethod(
                Constant.METHOD_MOVE_NEXT,
                new Type[] { });

            List<object> parsedValues = new List<object>();

            // enum each value
            while((bool)moveNextMethodInfo.Invoke(dictionaryEnumerator, new object[] {}))
            {
                parsedValues.Add(currentPropertyInfo.GetValue(dictionaryEnumerator, null));
            }

            return parsedValues;
        }

        /// <summary/>
        private static bool IsDictionary(Type type)
        {
            // if the type is not generic then move on cause its not a dictionary
            if (!type.IsGenericType)
            {
                return false;
            }

            Type[] arguments = type.GetGenericArguments();

            // if the type does not support two generic parameters then move on cause its not a dictionary
            if (arguments.Length != Constant.LENGTH_DICTIONARY_ARGUMENTS)
            {
                return false;
            }

            // build the matching dictionary type
            Type genericType = typeof(Dictionary<,>);
            Type dictionaryType = genericType.MakeGenericType(arguments);

            // if this is a dictionary type then handle it
            if (!dictionaryType.IsAssignableFrom(type))
            {
                return false;
            }

            return true;
        }
    }
}

